<?php
/**
 * | 节程 [ 节程赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 *  | Copyright (c) 2020~2029 温州惊蛰网络科技有限公司 All rights reserved.
 * +----------------------------------------------------------------------
 *  | Licensed 节程并不是自由软件，未经许可不能去掉节程相关版权
 * +----------------------------------------------------------------------
 */
declare (strict_types=1);

namespace app\index\service;

use app\index\model\Commodity;
use app\index\model\SkuInventory;
use app\index\util\OrderCount;
use app\index\util\Redis;
use app\utils\RedisUtil;
use think\Exception;
use think\exception\InvalidArgumentException;

class InventoryService
{

//    private $inventory;
    private $redis;
    private $commodityId;
    private $commodity;
    private $pvsValue;
    private $inventoryType;
    private $is_level;

    public function getInventoryType()
    {
        return $this->inventoryType;
    }

    public function __construct(int $commodityId)
    {
        global $user;
//        $this->redis = Redis::getInstance();
        $this->redis = new RedisUtil();
        $commodityData = $this->redis->get(checkRedisKey('COMMODITY:' . $commodityId));
        if (empty($commodityData)) {
            throw new InvalidArgumentException("商品数据不存在", HTTP_NOTACCEPT);
        }
        $this->commodity = json_decode($commodityData, true);
        $this->commodityId = $commodityId;
        $this->inventoryType = $this->commodity['inventory_type'];
        $this->is_level = $user['level'];
    }

    public function getCommodity()
    {
        return $this->commodity;
    }


    public function getAllInventory()
    {
        $result = [];
        if (empty($this->commodity['has_sku'])) {
            $key = "COMMODITY:" . $this->commodityId . ":SELL";
            $lLen = $this->redis->lLen(checkRedisKey($key));
            $result['inventory'] = $lLen;
        } else {

            foreach ($this->commodity['sku'] as $sku) {
                $key = "COMMODITY:" . $this->commodityId . ":SKU:" . $sku['id'] . ":SELL";
                $lLen = $this->redis->lLen(checkRedisKey($key));
                $result[] = array_merge($sku, ['inventory' => $lLen]);
            }
        }
        return $result;
    }

    public function getInventory(int $skuId = null)
    {
        if (empty($this->commodity['has_sku'])) {
            $key = "COMMODITY:" . $this->commodityId . ":SELL";
            $lLen = $this->redis->lLen(checkRedisKey($key));
        } else {
            $key = "COMMODITY:" . $this->commodityId . ":SKU:" . $skuId . ":SELL";
            $lLen = $this->redis->lLen(checkRedisKey($key));
        }
        return $lLen;
    }

    public function getPrice(int $skuId = null)
    {

        if (empty($this->commodity['has_sku'])) {
            $key = "COMMODITY:" . $this->commodityId . ":SELL";
            $price = $this->redis->lIndex(checkRedisKey($key), 0);
            $price = $this->getLevelPrice((string)$price);
        } else {
            $key = "COMMODITY:" . $this->commodityId . ":SKU:" . $skuId . ":SELL";
            $price = $this->redis->lIndex(checkRedisKey($key), 0);
            $price = $this->getLevelPrice((string)$price);
        }
        return $price;
    }

    public function subInventory(int $skuId = null)
    {
        $checkLimitation = $this->checkLimitation();
        if ($checkLimitation) {
            throw new Exception("商品超过限购", HTTP_NOTACCEPT);
        }

        if (empty($this->commodity['has_sku'])) {
            $key = "COMMODITY:" . $this->commodityId . ":SELL";
            $price = $this->redis->lPop(checkRedisKey($key));
            if (false != $price) {
                Commodity::where('id', $this->commodityId)->inc('sell')->update();
            }
        } else {
            $key = "COMMODITY:" . $this->commodityId . ":SKU:" . $skuId . ":SELL";
            $price = $this->redis->lPop(checkRedisKey($key));
            if (false != $price) {
                Commodity::where('id', $this->commodityId)->inc('sell')->update();
                SkuInventory::where(['sku_id' => $skuId])->inc('sell')->update();
            }
        }

        return $price;
    }

    public function subInventoryByCount(int $count, ?int $skuId = null)
    {

        $checkLimitation = $this->checkLimitation($count);
        if ($checkLimitation) {
            throw new Exception("商品超过限购", HTTP_NOTACCEPT);
        }
        if (empty($this->commodity['has_sku'])) {
            $key = "COMMODITY:" . $this->commodityId . ":SELL";
            $price = $this->subingInventory($count, $key);
            Commodity::where('id', $this->commodityId)->inc('sell', $count)->update();
        } else {
            foreach ($this->commodity['sku'] as $k => $v) {
                if ($v['id'] == $skuId)
                    $this->pvsValue = $v['pvs_value'];
            }
            $key = "COMMODITY:" . $this->commodityId . ":SKU:" . $skuId . ":SELL";
            $price = $this->subingInventory($count, $key);
            Commodity::where('id', $this->commodityId)->inc('sell', $count)->update();
            SkuInventory::where(['sku_id' => $skuId])->inc('sell', $count)->update();
        }

        return $price;
    }
    public function getLevelPrice1(int $count,?int $skuId = null)
    {
        if (empty($this->commodity['has_sku'])) {
            $key = "COMMODITY:" . $this->commodityId . ":SELL";
//            Commodity::where('id', $this->commodityId)->inc('sell', $count)->update();
        } else {
            foreach ($this->commodity['sku'] as $k => $v) {
                if ($v['id'] == $skuId)
                    $this->pvsValue = $v['pvs_value'];
            }
            $key = "COMMODITY:" . $this->commodityId . ":SKU:" . $skuId . ":SELL";
//            Commodity::where('id', $this->commodityId)->inc('sell', $count)->update();
//            SkuInventory::where(['sku_id' => $skuId])->inc('sell', $count)->update();
        }
        $price = $this->redis->lIndex(checkRedisKey($key), 0);
//        $price = $this->redis->lPop(checkRedisKey($key));
        if ($price != false) {
//            $this->redis->lPush(checkRedisKey($key),$price);
            $redis_price = $this->getLevelPrice($price);
        }
        return $redis_price ?? '';
    }
    public function checkLimitation($count = 1): bool
    {
        if (empty($this->commodity['is_limitation']))
            return false;
        $flag = false;
        switch ($this->commodity['limitation_type']) {
            case 1:
                if ($count > $this->commodity['limitation'])
                    $flag = true;
                break;
            case 2:
                $orderCount = new OrderCount();
                $orderCommodityCount = $orderCount->commodityByOrderCount((string)$this->commodityId);
                if (bcadd((string)$count, (string)$orderCommodityCount) > $this->commodity['limitation'])
                    $flag = true;
                break;
        }
        return $flag;
    }

    private function subingInventory($count, $key)
    {

        // 判断库存
        if ($this->redis->lLen(checkRedisKey($key)) - $count < 0) {
            $sku = empty($this->pvsValue) ? "" : ",sku(" . $this->pvsValue . ")";
            throw new Exception("库存不足:商品(" . $this->commodity['name'] . ")" . $sku, HTTP_NOTACCEPT);
        }
        
        $redisPrice = 0;
        $num = 0;

        for ($i = 1; $i <= $count; $i++) {
            $price = $this->redis->lPop(checkRedisKey($key));
            if ($price != false) {
                $price = $this->getLevelPrice($price);
                $redisPrice = $price;
                $num++;
            } else {
                $data = [];
                $data = array_pad($data, $num, $redisPrice);
                $this->redis->lPush(checkRedisKey($key), ...$data);
                $sku = empty($this->pvsValue) ? "" : ",sku(" . $this->pvsValue . ")";
                throw new Exception("库存不足:商品(" . $this->commodity['name'] . ")" . $sku, HTTP_NOTACCEPT);
            }
        }
        return $redisPrice;
    }

public function getLevelPrice(String $price) : float {
        $price = json_decode($price,true);
        $sell_price = $price['sell_price'];
        if (!empty($this->is_level) && !empty($price['level_price'])){
            $array = is_array($price['level_price']) ? $price['level_price'] : json_decode($price['level_price'],true);
            if($array['type'] == 1){
                $count = $this->is_level;
                do{
                    $is_bool = true;
                    foreach ($array['price'] AS $index => $item) {
                        if (empty($item['price']))break;
                        if ($item['level'] == $count) {
                            if ($item['type'] == 1)
                                $sell_price = $item['price'];
                            else
                                $sell_price = bcmul((string)((float)$item['price']/10), (string)$price['sell_price']);
                            $is_bool = false;
                            break;
                        }
                    }
                    $count--;
                }while($count > 0 && $is_bool);
            }elseif($array['type'] == 2){
                $discount = \think\facade\Db::name('membership_level')
                    ->where('level',$this->is_level)
                    ->where('status',1)
                    ->where('delete_time',null)
                    ->value('discount');
                $sell_price = bcmul((string)((float)$discount/10),(string)$price['sell_price']);
            }
        }
        $sell_price = bcmul((string)floor($sell_price * 1000),"0.001",2);
        return (float)$sell_price;
    }

    public function backInventory($num, $price,int $skuId = null)
    {

        if (empty($this->commodity['has_sku'])) {
            Commodity::where('id', $this->commodityId)->where('sell','>', $num)->dec('sell', $num)->update();
            $key = "COMMODITY:" . $this->commodityId . ":SELL";
            $data = [];
//            $price = $this->redis->lPop(checkRedisKey($key));
            $da = ["sell_price" => $this->commodity['sell_price'], "level_price" => Commodity::find($this->commodityId)['level_price']];

            $price = json_encode($da);

            $data = array_pad($data, $num, $price);

            $this->redis->lPush(checkRedisKey($key), ...$data);
        } else {
            Commodity::where('id', $this->commodityId)->where('sell','>', $num)->dec('sell', $num)->update();
            $skuInventory=SkuInventory::where(['sku_id' => $skuId])->find(); # ->dec('sell', $num)->update();
            if($skuInventory){
                if ($skuInventory->sell>0){
                    $skuInventory->sell=$skuInventory->sell-1;
                    $skuInventory->save();
                }

                $key = "COMMODITY:" . $this->commodityId . ":SKU:" . $skuId . ":SELL";

                $data = [];
                $da = ["sell_price" => $skuInventory->sell_price, "level_price" => $skuInventory->level_price];

                $data = array_pad($data, $num, json_encode($da));

                $this->redis->lPush(checkRedisKey($key), ...$data);
            }

        }
        return true;
    }


    public function backInventoryToCache($num, $price, int $skuId = null)
    {


        if (empty($this->commodity['has_sku'])) {
            $key = "COMMODITY:" . $this->commodityId . ":SELL";
            $data = [];
//            $price = $this->redis->lPop(checkRedisKey($key));
            $da = ["sell_price" => $this->commodity['sell_price'], "level_price" => Commodity::find($this->commodityId)['level_price']];
            $price = json_encode($da);
            $data = array_pad($data, $num, $price);
            $this->redis->lPush(checkRedisKey($key), ...$data);
        } else {
            $key = "COMMODITY:" . $this->commodityId . ":SKU:" . $skuId . ":SELL";
            $data = [];
//            $price = $this->redis->lPop(checkRedisKey($key));
            $skuInventory=SkuInventory::find($skuId);
            $da = ["sell_price" => $skuInventory['sell_price'], "level_price" => $skuInventory['level_price']];
            $price = json_encode($da);
            $data = array_pad($data, $num, $price);
            $this->redis->lPush(checkRedisKey($key), ...$data);
        }
        return true;
    }


}